{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'talib'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-4-8e4ab90b6458>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mtalib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabstract\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mSMA\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mcollections\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdeque\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mgmsdk\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'talib'"
     ]
    }
   ],
   "source": [
    "\n",
    "import time\n",
    "from talib.abstract import SMA\n",
    "import numpy as np\n",
    "from collections import dequee\n",
    "from gmsdk import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "Missing parentheses in call to 'print'. Did you mean print(\"received execution: %s\" % execution.exec_type)? (<ipython-input-3-e65dfb334fec>, line 52)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-3-e65dfb334fec>\"\u001b[0;36m, line \u001b[0;32m52\u001b[0m\n\u001b[0;31m    print \"received execution: %s\" % execution.exec_type\u001b[0m\n\u001b[0m                                 ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m Missing parentheses in call to 'print'. Did you mean print(\"received execution: %s\" % execution.exec_type)?\n"
     ]
    }
   ],
   "source": [
    "eps = 1e-6\n",
    "threshold = 0.235\n",
    "tick_size = 0.2\n",
    "half_tick_size = tick_size / 2\n",
    "significant_diff = tick_size * 2.6\n",
    "\n",
    "class MA(StrategyBase):\n",
    "\n",
    "    \"\"\" strategy example1: MA decision price cross long MA, then place a order, temporary reverse trends place more orders \"\"\"\n",
    "\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        #import pdb; pdb.set_trace()\n",
    "        super(MA, self).__init__(*args, **kwargs)\n",
    "        # 策略初始化工作在这里写，从外部读取静态数据，读取策略配置参数等工作，只在策略启动初始化时执行一次。\n",
    "\n",
    "        # 从配置文件中读取配置参数\n",
    "        self.exchange = self.config.get(\"para\", \"trade_exchange\")\n",
    "        self.sec_id = self.config.get(\"para\", \"trade_symbol\")\n",
    "        self.symbol = \".\".join([self.exchange, self.sec_id])\n",
    "        self.last_price = 0.0\n",
    "        self.trade_unit = [1.0, 2.0, 4.0, 8.0, 5.0, 3.0,2.0,1.0,1.0, 0.0] ##  [8.0, 4.0, 2.0, 1.0]\n",
    "        self.trade_count = 0\n",
    "        self.trade_limit = len(self.trade_unit)\n",
    "        self.window_size = self.config.getint(\"para\", \"window_size\") or 60\n",
    "        self.timeperiod = self.config.getint(\"para\", \"timeperiod\")\n",
    "        self.bar_type = self.config.getint(\"para\", \"bar_type\") \n",
    "        self.close_buffer = deque(maxlen=self.window_size)\n",
    "        self.significant_diff = self.config.getfloat(\"para\", \"significant_diff\") or significant_diff\n",
    "\n",
    "        # prepare historical bars for MA calculating\n",
    "        # 从数据服务中准备一段历史数据，使得收到第一个bar后就可以按需要计算ma\n",
    "        last_closes = [bar.close for bar in self.get_last_n_bars(self.symbol, self.bar_type, self.window_size)]\n",
    "        last_closes.reverse()     #因为查询出来的时间是倒序排列，需要倒一下顺序\n",
    "        self.close_buffer.extend(last_closes)\n",
    "\n",
    "    # 响应bar数据到达事件\n",
    "    def on_bar(self, bar):\n",
    "        # 确认下bar数据是订阅的分时\n",
    "        if bar.bar_type == self.bar_type:\n",
    "            # 把数据加入缓存\n",
    "            self.close_buffer.append(bar.close)\n",
    "            # 调用策略计算\n",
    "            self.algo_action()\n",
    "\n",
    "   # 响应tick数据到达事件\n",
    "    def on_tick(self, tick):\n",
    "        # 更新市场最新成交价\n",
    "        self.last_price = tick.last_price\n",
    "\n",
    "    def on_execution(self, execution):\n",
    "        #打印订单成交回报信息\n",
    "        print \"received execution: %s\" % execution.exec_type\n",
    "\n",
    "    #策略的算法函数，策略的交易逻辑实现部分\n",
    "    def algo_action(self):\n",
    "        #数据转换，方便调用ta-lib函数进行技术指标的计算，这里用SMA指标\n",
    "        close = np.asarray(self.close_buffer)\n",
    "        ma = SMA({\"close\":close}, timeperiod=self.timeperiod)\n",
    "        delta = round(close[-1] - ma[-1],4)     # 最新数据点，bar的收盘价跟ma的差\n",
    "        last_ma = round(ma[-1], 4)  #  均线ma的最新值\n",
    "        momentum = round(self.last_price - last_ma,4)  # 当前最新价格跟ma之间的差，成交价相对ma偏离\n",
    "        #print \"close: \", close\n",
    "        print(\"close ma delta: {0}, last_ma: {1}, momentum: {2}\".format(delta, last_ma, momentum))\n",
    "\n",
    "        a_p = self.get_position(self.exchange, self.sec_id, OrderSide_Ask)    #查询策略所持有的空仓\n",
    "        b_p = self.get_position(self.exchange, self.sec_id, OrderSide_Bid)    #查询策略所持有的多仓\n",
    "        # 打印持仓信息\n",
    "        print (\"pos long: {0} vwap: {1}, pos short: {2}, vwap: {3}\".format(b_p.volume if b_p else 0.0,\n",
    "                round(b_p.vwap,2) if b_p else 0.0,\n",
    "                a_p.volume if a_p else 0.0,\n",
    "                round(a_p.vwap,2) if a_p else 0.0))\n",
    "        if delta > threshold and momentum >= significant_diff:        ## 收盘价上穿均线，且当前价格偏离满足门限过滤条件，多信号\n",
    "            # 没有空仓，且没有超出下单次数限制\n",
    "            if (a_p is None or a_p.volume < eps) and self.trade_count < self.trade_limit:\n",
    "                # 依次获取下单的交易量，下单量是配置的一个整数数列，用于仓位管理，可用配置文件中设置\n",
    "                vol = self.trade_unit[self.trade_count]\n",
    "                # 如果本次下单量大于0,  发出买入委托交易指令\n",
    "                if vol > eps:\n",
    "                    self.open_long(self.exchange, self.sec_id, self.last_price, vol)\n",
    "                self.trade_count += 1    #增加计数\n",
    "            else:\n",
    "                #  如果有空仓，且达到本次信号的交易次数上限\n",
    "                if a_p and a_p.volume > eps and self.trade_count == self.trade_limit:\n",
    "                    self.close_short(self.exchange, self.sec_id, self.last_price, a_p.volume)    # 平掉所有空仓\n",
    "                    self.trade_count = 0\n",
    "                else:\n",
    "                    # 有空仓时，且上次交易信号后没达到交易次数限制，继续加空\n",
    "                    vol = self.trade_unit[self.trade_count] if self.trade_count < self.trade_limit else 0.0\n",
    "                    self.trade_count += 1\n",
    "                    if vol > eps:\n",
    "                        self.open_short(self.exchange, self.sec_id,self.last_price, vol)\n",
    "        elif delta < -threshold and momentum <= - significant_diff:     ## bar 收盘价下穿ma均线，且偏离满足信号过滤条件\n",
    "            # 没有多仓时，开空\n",
    "            if (b_p is None or b_p.volume < eps) and self.trade_count < self.trade_limit:\n",
    "                vol = self.trade_unit[self.trade_count]\n",
    "                self.trade_count += 1\n",
    "                if vol > eps:\n",
    "                    self.open_short(self.exchange, self.sec_id, self.last_price, vol)\n",
    "            else:\n",
    "                # 已有多仓，且达到了交易次数限制，平掉多仓\n",
    "                if b_p and b_p.volume > eps and self.trade_count == self.trade_limit:\n",
    "                    self.close_long(self.exchange, self.sec_id, self.last_price, b_p.volume)\n",
    "                    self.trade_count = 0\n",
    "                else:\n",
    "                    # 已有多仓，且没有达到交易次数限制，继续加多\n",
    "                    vol = self.trade_unit[self.trade_count] if self.trade_count < self.trade_limit else 0.0\n",
    "                    self.trade_count += 1\n",
    "                    if vol > eps:\n",
    "                        self.open_long(self.exchange, self.sec_id, self.last_price, vol)\n",
    "        else:       ##  其他情况，忽略不处理\n",
    "            ## get positions and close if any\n",
    "            #self.trade_count = 0   ## reset trade count\n",
    "            pass\n",
    "\n",
    "# 策略启动入口\n",
    "if __name__ == \"__main__\":\n",
    "    #  初始化策略\n",
    "    ma = MA(config_file=\"strategy_ma.ini\")\n",
    "    #import pdb; pdb.set_trace()   # python调试开关\n",
    "    print \"strategy ready, waiting for market data ......\"\n",
    "    # 策略进入运行，等待数据事件\n",
    "    ret = ma.run()\n",
    "    # 打印策略退出状态\n",
    "    print \"MA :\", ma.get_strerror(ret)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
